﻿#include "as_mysql_connection.h"
#include "as_mysql_result.h"
#include <assert.h>
#include <mysql.h>
#include <thread>
#include <iostream>
#include <future>

as::db::MysqlScopeInit::MysqlScopeInit(int argc, char** argv, char** group)
{
	mysql_library_init(argc, argv, group);
}

as::db::MysqlScopeInit::~MysqlScopeInit()
{
	mysql_library_end();
}

as::db::MysqlConnection::MysqlConnection(const MysqlConnConfig& conf)
	:conf_(conf)
{
	conn_ = mysql_init(nullptr);
	assert(conn_);

	auto result = mysql_real_connect(conn_, conf.host_.c_str(), conf.user_.c_str(), conf.pw_.c_str(), conf.databases_.c_str(), conf.port_, NULL, 0);
	if (result == nullptr)
	{
		CloseConn(CONNECT_ERROR, "init error");
	}
}

as::db::MysqlConnection::MysqlConnection(MYSQL* conn, const MysqlConnConfig& conf)
	:conn_(conn), conf_(conf)
{
	assert(conn_);
}

as::db::MysqlConnection::~MysqlConnection()
{
	if (conn_)
		mysql_close(conn_);
}


std::shared_ptr<as::db::MysqlResult> as::db::MysqlConnection::Exce(const std::string_view& cmd)
{
	if (IsConnect() == false)
	{
		if (IsAutoReconnect())
		{
			if (Reconnect() == false)
			{
				return nullptr;
			}
		}
	}

	int ret = mysql_query(conn_, cmd.data());
	if (ret != 0)
	{
		SetError(QUERY_ERROR, mysql_error(conn_));
		return nullptr;
	}

	auto result = mysql_store_result(conn_);
	if (result == nullptr)
	{
		auto err_num = mysql_errno(conn_);
		if (err_num != 0)
		{
			SetError(QUERY_ERROR, mysql_error(conn_));
			return nullptr;
		}

		return std::make_shared<as::db::MysqlResult>(nullptr);
	}

	return std::make_shared<as::db::MysqlResult>(result);
}

as::db::QueryAwait as::db::MysqlConnection::co_exce(const std::string_view& cmd)
{
	std::cout << "co_exce:" << std::this_thread::get_id() << "\n";
	auto awaiter = as::db::QueryAwait{ this, cmd };
	return awaiter;
}

bool as::db::MysqlConnection::IsAutoReconnect() const
{
	return conf_.auto_reconnect_;
}

bool as::db::MysqlConnection::IsConnect() const
{
	if (conn_ == nullptr)
		return false;
	int ret = mysql_ping(conn_);
	return ret == 0;
}

as::db::MysqlConnection::Err as::db::MysqlConnection::GetError() const
{
	return err_;
}

const char* as::db::MysqlConnection::GetErrorLog() const
{
	return err_str_.c_str();
}

bool as::db::MysqlConnection::Reconnect()
{
	auto result = mysql_real_connect(conn_, conf_.host_.c_str(), conf_.user_.c_str(), conf_.pw_.c_str(), conf_.databases_.c_str(), conf_.port_, NULL, 0);
	if (result == nullptr)
	{
		CloseConn(CONNECT_ERROR, "reconnect error");
		return false;
	}
	return true;
}

void as::db::MysqlConnection::CloseConn(Err log, const std::string_view& errlog)
{
	mysql_close(conn_);
	conn_ = nullptr;
	SetError(log, errlog);
}

void as::db::MysqlConnection::SetError(Err log, const std::string_view& errlog)
{
	err_ = log;
	err_str_ = errlog.data();
}

as::db::MysqlResult* as::db::MysqlConnection::AllocResult(MYSQL_RES* res)
{
	return new MysqlResult(res);
}

void as::db::MysqlConnection::ReleaseResult(MysqlResult* result)
{
	delete result;
}


bool as::db::QueryAwait::await_ready() noexcept
{
	std::cout << "await_ready(), thread_id: " << std::this_thread::get_id() << std::endl;
	return res_ != nullptr;
}

void as::db::QueryAwait::await_suspend(std::coroutine_handle<QueryPromise::promise_type> handle) noexcept
{
	std::cout << "await_suspend(), thread_id: " << std::this_thread::get_id() << std::endl;
	{
		auto fufun = std::async(std::launch::async | std::launch::deferred, [this, handle]() {
			std::cout << "await_suspend(), done thread_id: " << std::this_thread::get_id() << std::endl;

			res_ = conn_->Exce(cmd_);
			//协程执行完,唤醒调用方
			handle.resume();
			}
		);
		fufun.get();
	}
}

std::shared_ptr<as::db::MysqlResult> as::db::QueryAwait::await_resume() noexcept
{
	std::cout << "await_resume(), thread_id: " << std::this_thread::get_id() << std::endl;
	return res_;
}
